package camera;

import camera_controller.CameraController;
import algos.Geometry;

import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;

/**
 * This is IP camera class implementation Inherit from {@link AbstractCamera}
 * 
 * @author Moamen Mokhtar
 * 
 */
public class IPCam extends AbstractCamera {

    private final double PanAngle = 5.5d; // value in degrees of a single pan
    // move at speed equals -4
    private double currentAngle = 0;
    private int radius = 200;
    private final int speed = -4; // indicator to value of a single pan
    private CameraController controller; // camera controller that gives moving
    // orders to camera
    private int threshold = 9;
    private double rightPan = 0;
    private final double MAX_PAN = 150;
    private int moveTo; // 0 pan right & 1 pan left
    public String ip;
    private final int RIGHT = 0, LEFT = 1;
    private int zoomLevel;
    private final float zoomThreshold = 60.0f;
    private int width;
    private double defaultAngle;
    private int defaultThreshold;
    private int defaultRightBan;
    private Vector3f defaultDirection;
    /* boolean if set an HTTP request is sent to the camera controller, else no request is sent.
     * Set to true if real cameras exists, if working offline(Testing) set to false;
     */
    private boolean cameraMove = false;

    /**
     * Constructs an IPCam object
     * 
     * @param pos
     *            camera position
     * @param dir
     *            camera default direction
     * @param ip
     *            camera IP
     */
    public IPCam(Vector3f pos, Vector3f dir, String ip, String name, int width) {
        super.name = name;
        super.pos = pos;
        super.dir = dir;
        defaultDirection = dir.clone();
        this.ip = ip;
        controller = new CameraController(ip);
        currentAngle = Math.toDegrees(Geometry.computeAngle(new double[]{
                    pos.x, pos.z, 0}, new double[]{dir.x, dir.z, 0},
                new double[]{640, pos.z, 0}));

        defaultThreshold = threshold;
        defaultRightBan = 0;
        int quadrant = checkQuad(pos, dir);
        if (quadrant > 2) {
            currentAngle = 360 - currentAngle;
        }
        defaultAngle = currentAngle;
        this.width = width;
    }

    /**
     * returns the quadrant of camera direction angle
     * 
     * @param pos
     *            vector3f of position vector of the camera
     * @param dir
     *            vector3f of direction vector of the camera
     */
    private int checkQuad(Vector3f pos, Vector3f dir) {
        // TODO Auto-generated method stub

        if (dir.x >= pos.x && dir.z <= pos.z) {
            return 1;
        }
        if (dir.x <= pos.x && dir.z <= pos.z) {
            return 2;
        }
        if (dir.x <= pos.x && dir.z >= pos.z) {
            return 3;
        }
        return 4;

    }

    /**
     * updates camera direction by calling pan commands It calculates the
     * quadrant of the object angle measured from the origin(camera position)
     * with the x-axis and get quadrant of the camera direction angle based on
     * the angle of camera direction and object a pan left or right command is
     * taken.
     */
    @Override
    public void updateCamera(Vector3f modelPosition) {

        double objAngle = Math.toDegrees(Geometry.computeAngle(new double[]{
                    pos.x, pos.z, 0}, new double[]{640, pos.z, 0},
                new double[]{modelPosition.x, modelPosition.z, 0}));
        int quad = checkQuad(pos, modelPosition);
        if (quad > 2) {
            objAngle = 360 - objAngle;
        }
        double angle = Math.abs(objAngle - currentAngle);
        int currentQuad = 1;
        if (currentAngle > 90 && currentAngle <= 180) {
            currentQuad = 2;
        } else if (currentAngle > 180 && currentAngle <= 270) {
            currentQuad = 3;
        } else if (currentAngle > 270) {
            currentQuad = 4;
        }
        float distance = pos.distance(modelPosition);
        if (cameraMove) {
            if (Float.compare(distance, zoomThreshold) >= 0) {
                if (zoomLevel != 1) {
                    zoomLevel = 1;
                    threshold = 6;
                    controller.zoomIn();
                }
            } else if (zoomLevel == 1) {
                zoomLevel = 0;
                threshold = 9;
                controller.zoomOut();
            }
        }
        if (angle >= threshold) {
            switch (quad) {
                case 1: // the object is in the first quad
                    switch (currentQuad) {
                        case 1: // the camera direction is in the first quad
                            if (currentAngle < objAngle) {// if the camera direction
                                // angle is less than the
                                // object position angle
                                // move left
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }// if the camera can pan in this direction
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;

                            } else {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            }
                            break;
                        case 2:// the camera direction is in the second quad
                            // pan right
                            rightPan += PanAngle;
                            if (rightPan > MAX_PAN) {
                                rightPan -= PanAngle;
                                return;
                            }
                            currentAngle -= PanAngle;
                            if (currentAngle < 0) {
                                currentAngle = 360 + currentAngle;
                            }
                            // controller.moveRight(speed);
                            moveTo = RIGHT;
                            break;
                        case 3:
                            // the camera direction is in the third quad
                            // check the current angle if it is nearer to the second
                            // quad
                            // pan righ
                            // else pan left
                            if (currentAngle <= 225) {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            } else {
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            }
                            break;
                        case 4:// if camera direction is in the fourth quad
                            rightPan -= PanAngle;
                            if (Math.abs(rightPan) > MAX_PAN) {
                                rightPan += PanAngle;
                                return;
                            }
                            currentAngle = (currentAngle + PanAngle) % 360;
                            // controller.moveLeft(speed);
                            moveTo = LEFT;
                            break;
                    }
                    break;

                case 2:// the object is in the second quad.
                    switch (currentQuad) {
                        case 1:
                            rightPan -= PanAngle;
                            if (Math.abs(rightPan) > MAX_PAN) {
                                rightPan += PanAngle;
                                return;
                            }
                            currentAngle = (currentAngle + PanAngle) % 360;
                            // controller.moveLeft(speed);
                            moveTo = LEFT;
                            break;
                        case 2:
                            if (currentAngle < objAngle) {
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                // move left
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            } else {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            }
                            break;
                        case 3:
                            rightPan += PanAngle;
                            if (rightPan > MAX_PAN) {
                                rightPan -= PanAngle;
                                return;
                            }
                            currentAngle -= PanAngle;
                            if (currentAngle < 0) {
                                currentAngle = 360 + currentAngle;
                            }
                            // controller.moveRight(speed);
                            moveTo = RIGHT;
                            break;

                        case 4:

                            if (currentAngle <= 315) {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            } else {
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            }
                            break;
                    }
                    break;

                case 3:
                    switch (currentQuad) {
                        case 1:
                            if (currentAngle <= 45) {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            } else {
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            }
                            break;
                        case 2:
                            rightPan -= PanAngle;
                            if (Math.abs(rightPan) > MAX_PAN) {
                                rightPan += PanAngle;
                                return;
                            }
                            currentAngle = (currentAngle + PanAngle) % 360;
                            // controller.moveLeft(speed);
                            moveTo = LEFT;
                            break;
                        case 3:
                            if (currentAngle < objAngle) {
                                // move left
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            } else {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            }
                            break;
                        case 4:
                            rightPan += PanAngle;
                            if (rightPan > MAX_PAN) {
                                rightPan -= PanAngle;
                                return;
                            }
                            currentAngle -= PanAngle;
                            if (currentAngle < 0) {
                                currentAngle = 360 + currentAngle;
                            }
                            // controller.moveRight(speed);
                            moveTo = RIGHT;
                            break;
                    }
                    break;

                case 4:
                    switch (currentQuad) {
                        case 1:
                            rightPan += PanAngle;
                            if (rightPan > MAX_PAN) {
                                rightPan -= PanAngle;
                                return;
                            }
                            currentAngle -= PanAngle;
                            if (currentAngle < 0) {
                                currentAngle = 360 + currentAngle;
                            }
                            // controller.moveRight(speed);
                            moveTo = RIGHT;
                            break;
                        case 2:
                            if (currentAngle <= 135) {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            } else {
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            }
                            break;
                        case 3:
                            rightPan -= PanAngle;
                            if (Math.abs(rightPan) > MAX_PAN) {
                                rightPan += PanAngle;
                                return;
                            }
                            currentAngle = (currentAngle + PanAngle) % 360;
                            // controller.moveLeft(speed);
                            moveTo = LEFT;
                            break;
                        case 4:
                            if (currentAngle < objAngle) {
                                // move left
                                rightPan -= PanAngle;
                                if (Math.abs(rightPan) > MAX_PAN) {
                                    rightPan += PanAngle;
                                    return;
                                }
                                currentAngle = (currentAngle + PanAngle) % 360;
                                // controller.moveLeft(speed);
                                moveTo = LEFT;
                            } else {
                                rightPan += PanAngle;
                                if (rightPan > MAX_PAN) {
                                    rightPan -= PanAngle;
                                    return;
                                }
                                currentAngle -= PanAngle;
                                if (currentAngle < 0) {
                                    currentAngle = 360 + currentAngle;
                                }
                                // controller.moveRight(speed);
                                moveTo = RIGHT;
                            }
                            break;
                    }
                    break;
            }

            dir.x = (float) (radius * Math.cos(Math.toRadians(currentAngle)) + pos.x);
            dir.z = (float) (radius * Math.sin(Math.toRadians(currentAngle)));
            dir.z = pos.z - dir.z;
            if (cameraMove) {
                if (moveTo == RIGHT) {
                    controller.moveRight(speed);
                } else {
                    controller.moveLeft(speed);
                }
            }
        }
    }

    @Override
    public void reset() {
        // TODO Auto-generated method stub
        currentAngle = defaultAngle;
        threshold = defaultThreshold;
        rightPan = defaultRightBan;
        zoomLevel = 0;
        dir = defaultDirection.clone();
        controller.moveHome();
    }

    @Override
    protected void zoom(float value) {
        // TODO Auto-generated method stub
    }

    @Override
    public Vector3f getLocation() {
        // TODO Auto-generated method stub
        return pos;
    }

    @Override
    public Vector3f getDirection() {
        // TODO Auto-generated method stub
        return dir;
    }

    @Override
    public Camera getCam() {
        // TODO Auto-generated method stub
        // not implemented
        return null;
    }

    @Override
    public void setViewPort(float[] viewPortVals) {
        // TODO Auto-generated method stub
        // not implemented
    }

    @Override
    public ViewPort getViewPort() {
        // TODO Auto-generated method stub
        // not implemented
        return null;
    }
}
